/**
 * Created by ference on 2018/8/2.
 */

'use strict';
const xml2js = require('xml2js');
const crypto = require('crypto');
const rawBody = require('raw-body');
exports.raw = args => {
  let keys = Object.keys(args);
  keys = keys.sort();
  const newArgs = {};
  keys.forEach(function(key) {
    newArgs[key] = args[key];
  });
  let string = '';
  for (const k in newArgs) {
    string += '&' + k + '=' + newArgs[k];
  }
  string = string.substr(1);
  return string;
};

exports.parseXML = async xml => {
  const result = new Promise(function(resolve, reject) {
    xml2js.parseString(xml, {
      trim: true,
      explicitArray: false,
    }, (err, result) => {
      if (result) {
        if (result.xml) {
          resolve(result.xml);
        } else if (result.root) {
          resolve(result.root);
        }
      } else {
        reject(err);
      }
    });
  });
  return result;
};

exports.buildXML = (json, opts) => {
  const options = opts || { allowSurrogateChars: true };
  const builder = new xml2js.Builder(options);
  return builder.buildObject(json);
};
exports.padding = n => {
  let len = n % 32;
  if (len === 0) {
    len = 32;
  } else {
    len = 32 - len;
  }
  const buf = new Buffer(len);
  for (let i = 0; i < len; i++) {
    buf[i] = len;
  }
  return buf;
};
exports.htonl = n => {
  const buf = new Buffer(4);
  buf[0] = (n & 0xFF000000) >> 24;
  buf[1] = (n & 0x00FF0000) >> 16;
  buf[2] = (n & 0x0000FF00) >> 8;
  buf[3] = (n & 0x000000FF) >> 0;
  return buf;
};

exports.randomPrefix = n => {
  const _str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
  const buf = new Buffer(n);
  for (let i = 0; i < n; i++) {
    buf[i] = _str.charCodeAt(Math.floor(Math.random() * _str.length));
  }
  return buf;
};
exports.getNonceStr = (length = 32) => {
  const chars =
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const maxPos = chars.length;
  let noceStr = '';
  for (let i = 0; i < (length); i++) {
    noceStr += chars.charAt(Math.floor(Math.random() * maxPos));
  }
  if (noceStr[0] === 0) return this.getNonceStr();
  return noceStr;
};

exports.decryptForAES256ECB = (_encryptedData, key, iv) => {
  const clearEncoding = 'utf8';
  const cipherEncoding = 'base64';
  const cipherChunks = [];
  const decipher = crypto.createDecipheriv('aes-256-ecb', key, iv);
  decipher.setAutoPadding(true);
  cipherChunks.push(decipher.update(_encryptedData, cipherEncoding, clearEncoding));
  cipherChunks.push(decipher.final(clearEncoding));
  const result = cipherChunks.join('');
  return result;
};

exports.decryptForAES128CBC = (_sessionKey, _encryptedData, _iv) => {
  const sessionKey = new Buffer(_sessionKey, 'base64');
  const encryptedData = new Buffer(_encryptedData, 'base64');
  const iv = new Buffer(_iv, 'base64');
  let decoded;
  const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv);
  // 设置自动 padding 为 true，删除填充补位
  decipher.setAutoPadding(true);
  decoded = decipher.update(encryptedData, 'binary', 'utf8');
  decoded += decipher.final('utf8');
  decoded = JSON.parse(decoded);
  return decoded;
};
exports.encryptForAES256CBC = (msg, aesKey, iv, appId) => {
  const msgBuf = new Buffer(msg, 'utf-8');
  const msgBufLength = msgBuf.length;
  const preBuf = this.randomPrefix(16);
  const netBuf = this.htonl(msgBufLength);
  const corpBuf = new Buffer(appId, 'utf-8');
  const corpBufLength = corpBuf.length;
  const paddingBuf = this.padding(20 + msgBufLength + corpBufLength);
  const cipher = crypto.createCipheriv('aes-256-cbc', aesKey, iv);
  cipher.setAutoPadding(false); // 取消自动填充
  const encrypt = cipher.update(Buffer.concat([ preBuf, netBuf, msgBuf, corpBuf, paddingBuf ]), 'binary', 'base64') + cipher.final('base64');
  return encrypt;
};
exports.decryptForAES256CBC = (_encryptedData, aesKey, iv) => {
  const aesCipher = crypto.createDecipheriv('aes-256-cbc', aesKey, iv);
  aesCipher.setAutoPadding(false);
  const decipheredBuff = Buffer.concat([ aesCipher.update(_encryptedData, 'base64'), aesCipher.final() ]);
  const len_netOrder_corpid = decipheredBuff.slice(16);
  const msg_len = len_netOrder_corpid.slice(0, 4).readUInt32BE(0);
  const result = len_netOrder_corpid.slice(4, msg_len + 4).toString();
  return result;
};
exports.rawBody = async req => {
  const xml = await rawBody(req, {
    length: this.length,
    limit: '1mb',
    encoding: 'utf8',
  });
  return xml;
};
